//--------------------------------------------------------------------------------
// This file is a portion of the D2D1Renderer.  It is distributed
// under the MIT License, available in the root of this distribution and 
// at the following URL:
//
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright (c) Mirco Timmermann
//
//--------------------------------------------------------------------------------
#include "D2D1Renderer.h"
#include "canvas\Direct2DContext.h"

#include <string>

//http://www.gamedev.net/topic/552795-solved-d3d11-and-direct2d/

//////////////////////////////////////////////parallel nSight Debugger Problem/////////////////////////////////////////////////
//there is a problem with Direct2D when using nSight Debugger. Specially the mutex of Direct2D DirectX11 port. So the part must 
//be removed when running nSight. This means Direct2D must be set as off. (no creation of a direct2D instance when using mutex)
//__NSIGHT -> 0 = default, 1 = allow nsight debugging.
//#define __NSIGHT 0
//////////////////////////////////////////////parallel nSight Debugger Problem/////////////////////////////////////////////////

D2D1Renderer* D2D1Renderer::m_spRenderer = 0;

D2D1Renderer::D2D1Renderer() {
	if ( m_spRenderer == 0 ) {
		m_spRenderer = this;
	}

	_adapter = 0;

	_d2dFactory = 0;
	_deviceDX10_1 = 0;

	_sharedTexture_DX11 = 0;

	_DXGIKeyedMutex_DX10 = 0;
	_DXGIKeyedMutex_DX11 = 0;

	_direct2DContext = 0;
	_d2dRenderTarget = 0;

	_dWriteFactory = 0;
	_textFormat = 0;
}

D2D1Renderer::~D2D1Renderer(void) {
	if(_direct2DContext != 0) {
		delete _direct2DContext;
		_direct2DContext = 0;
	}

	if(_d2dFactory != 0) {
		_d2dFactory->Release();
		_d2dFactory = 0;
	}

	if(_deviceDX10_1 != 0) {
		_deviceDX10_1->Release();
		_deviceDX10_1 = 0;
	}

	if(_sharedTexture_DX11 != 0) {
		_sharedTexture_DX11->Release();
		_sharedTexture_DX11 = 0;
	}

	if(_DXGIKeyedMutex_DX10 != 0) {
		_DXGIKeyedMutex_DX10->Release();
		_DXGIKeyedMutex_DX10 = 0;
	}

	if(_DXGIKeyedMutex_DX11 != 0) {
		_DXGIKeyedMutex_DX11->Release();
		_DXGIKeyedMutex_DX11 = 0;
	}

	if(_d2dRenderTarget != 0) {
		_d2dRenderTarget->Release();
		_d2dRenderTarget = 0;
	}

	if(_dWriteFactory != 0) {
		_dWriteFactory->Release();
		_dWriteFactory = 0;
	}

	if(_textFormat != 0) {
		_textFormat->Release();
		_textFormat = 0;
	}
}

bool D2D1Renderer::Initialize(ID3D11Device* device, int width, int height) {
	//
	IDXGIFactory1* factory = 0;
	IDXGIOutput* adapterOutput = 0;

	HRESULT hr = S_OK;

	//Create a DirectX graphics interface factory.
	hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory);
	if(FAILED(hr)) {
		return false;
	}

	//Use the factory to create an adapter for the primary graphics interface (video card).
	hr = factory->EnumAdapters1(0, &_adapter);
	if(FAILED(hr)) {
		return false;
	}
//

	//create a directX10 device.
	CreateDirectX10Device();

	//
	createDirectX11Texture(device, width, height);

	//create surface sharing (Direct2D to DirectX11)
	createSharedSurface(width, height); //backBufferDesc.Width, backBufferDesc.Height;

	//for text rendering.
	createTextLayout(); 

	//creates a context object that encapsulate/holds things like pencil, color etc.... (so it doesnt need to create all the time(like HTML5))
	createDirect2DContext(); 

	factory->Release();
	_adapter->Release();

	return true;
}

bool D2D1Renderer::Initialize(ID3D11Texture2D* sharedTextureDX11) {
	//
	IDXGIFactory1* factory = 0;
	IDXGIOutput* adapterOutput = 0;

	HRESULT hr = S_OK;

	//Create a DirectX graphics interface factory.
	hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory);
	if(FAILED(hr)) {
		return false;
	}

	//Use the factory to create an adapter for the primary graphics interface (video card).
	hr = factory->EnumAdapters1(0, &_adapter);
	if(FAILED(hr)) {
		return false;
	}
//
	//create a directX10 device.
	CreateDirectX10Device();

	//
	_sharedTexture_DX11 = sharedTextureDX11;
	D3D11_TEXTURE2D_DESC desc;
	_sharedTexture_DX11->GetDesc(&desc);

	//create surface sharing (Direct2D to DirectX11)
	createSharedSurface(desc.Width , desc.Height); //backBufferDesc.Width, backBufferDesc.Height;

	//for text rendering.
	createTextLayout(); 

	//creates a context object that encapsulate/holds things like pencil, color etc.... (so it doesnt need to create all the time(like HTML5))
	createDirect2DContext(); 

	factory->Release();
	_adapter->Release();

	return true;
}

void D2D1Renderer::CreateDirect2DDevice(IDXGISurface* surface) {
	HRESULT hr;

// Create D2D factory
	hr = D2D1CreateFactory(D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), (void**)&_d2dFactory);
#ifdef _DEBUG
	if(FAILED(hr)) {
		return;
	}
#endif

	// Create D2D render target from the surface for the shared texture, which was opened in D3D10.1
	D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties;
	
	ZeroMemory(&renderTargetProperties, sizeof(renderTargetProperties));
	renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE::D2D1_RENDER_TARGET_TYPE_HARDWARE;
	renderTargetProperties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT::DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE::D2D1_ALPHA_MODE_PREMULTIPLIED);
	
	hr = _d2dFactory->CreateDxgiSurfaceRenderTarget(surface, &renderTargetProperties, &_d2dRenderTarget);
#ifdef _DEBUG
	if(FAILED(hr)) {
		return;
	}
#endif
}

void D2D1Renderer::Update() {
	BeginScene(0.0f, 0.0f, 0.0f, 0.0f);
	
	//_direct2DContext->begin();

	for(std::vector<ID2D1FrameRenderListener*>::iterator iter = _D2D1FrameListeners.begin(); iter != _D2D1FrameListeners.end(); ++iter) {
		(*iter)->OnD2D1FrameRender(_direct2DContext);
	}

	//_direct2DContext->end();

	EndScene();
}

/*
* creates a DirectX10 Device.
*/
void D2D1Renderer::CreateDirectX10Device() {
	D3D10_FEATURE_LEVEL1 featureLevel = D3D10_FEATURE_LEVEL1::D3D10_FEATURE_LEVEL_10_1;

	HRESULT hr = D3D10CreateDevice1(
		_adapter,
		D3D10_DRIVER_TYPE::D3D10_DRIVER_TYPE_HARDWARE,
		0,
		D3D10_CREATE_DEVICE_FLAG::D3D10_CREATE_DEVICE_DEBUG | D3D10_CREATE_DEVICE_FLAG::D3D10_CREATE_DEVICE_BGRA_SUPPORT | D3D10_CREATE_DEVICE_FLAG::D3D10_CREATE_DEVICE_SINGLETHREADED,
		featureLevel,
		D3D10_1_SDK_VERSION,
		&_deviceDX10_1
	);

#ifdef _DEBUG
	if(FAILED(hr)) {
		return;
	} else {
		//adds a name for the "D3D10 WARNING: Live"-Debug Output.
		const char c_szName[] = "D2D1Renderer::CreateDirectX10Device->_deviceDX10_1";
		_deviceDX10_1->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(c_szName)-1, c_szName);

		//adds a name for the "D3D10 WARNING: Live"-Debug Output.
		const char c_szName2[] = "D2D1Renderer::CreateDirectX10Device->_adapter";
		_adapter->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(c_szName2)-1, c_szName2);
	}
#endif
}

/////////////////////////////////////

void D2D1Renderer::createDirectX11Texture(ID3D11Device* deviceDX11, int width, int height) {
	/* (Shared Surface) - DX11
* API that consumes the Producer data.
*/
//OPEN CONSUMER
	//create a directX11 texture to render on it.
	HRESULT hr;

	// Create the shared texture to draw D2D content to
	D3D11_TEXTURE2D_DESC desc;
	ZeroMemory(&desc, sizeof(desc));

	desc.Width = width;
	desc.Height = height;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM;
	desc.SampleDesc.Count = 1;
	desc.Usage = D3D11_USAGE::D3D11_USAGE_DEFAULT;
	desc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_RENDER_TARGET | D3D11_BIND_FLAG::D3D11_BIND_SHADER_RESOURCE;
	desc.MiscFlags = D3D11_RESOURCE_MISC_FLAG::D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;

	hr = deviceDX11->CreateTexture2D(&desc, NULL, &_sharedTexture_DX11);
#ifdef _DEBUG
	if(FAILED(hr)) {
		return;
	} else {
		//adds a name for the "D3D10 WARNING: Live"-Debug Output.
		const char c_szName[] = "D2D1Renderer::createSharedSurface->_sharedTexture_DX11";
		_sharedTexture_DX11->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(c_szName)-1, c_szName);
	}
#endif
}

void D2D1Renderer::createSharedSurface(int width, int height) {
//
	HRESULT hr;
	HANDLE sharedHandle;

//create a shared handle.
	//Get the shared handle needed to open the shared texture in D3D10.1
	IDXGIResource* sharedResource11 = 0;

	hr = _sharedTexture_DX11->QueryInterface(__uuidof(IDXGIResource), (void**)&sharedResource11);
#ifdef _DEBUG
	if(FAILED(hr)) {
		return;
	} else {
		//adds a name for the "D3D10 WARNING: Live"-Debug Output.
		const char c_szName[] = "D2D1Renderer::createSharedSurface->sharedResource11";
		sharedResource11->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(c_szName)-1, c_szName);
	}
#endif

	sharedResource11->GetSharedHandle(&sharedHandle);
	if(FAILED(hr)) {
		return;
	}

	sharedResource11->Release();

/* (Shared Surface) - DX10
* API that produce the Consumer data.
*/
	IDXGISurface1* sharedSurface10 = 0;

	//open the surface for the shared texture in D3D10.1
	hr = _deviceDX10_1->OpenSharedResource(sharedHandle, __uuidof(IDXGISurface1), (void**)&sharedSurface10);

#ifdef _DEBUG
	if(FAILED(hr)) {
		return;
	} else {
		//adds a name for the "D3D10 WARNING: Live"-Debug Output.
		const char c_szName[] = "D2D1Renderer::createSharedSurface->sharedSurface10";
		sharedSurface10->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(c_szName)-1, c_szName);
	}
#endif

///////
	//get the keyed mutex for the shared texture (for D3D10.1)
	CreateKeyedMutex10(sharedSurface10);

	//get the keyed mutex for the shared texture (for D3D11)
	CreateKeyedMutex11(_sharedTexture_DX11);

///////
//CREATE DIRECT2D
	//create the Direct2D device.
	CreateDirect2DDevice(sharedSurface10);

///////
	//tell the debug output to bQueryInterfacee quiet..
	_deviceDX10_1->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);

	sharedSurface10->Release();
}

void D2D1Renderer::CreateKeyedMutex10(IUnknown* unknown) {
	HRESULT hr = S_OK;

	hr = unknown->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&_DXGIKeyedMutex_DX10);
	if(FAILED(hr)) {
		return;
	}
}

void D2D1Renderer::CreateKeyedMutex11(IUnknown* unknown) {
	HRESULT hr;

	hr = unknown->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&_DXGIKeyedMutex_DX11);
	if(FAILED(hr)) {
		return;
	}
}

ID3D11Texture2D* D2D1Renderer::GetTexture() {
	return _sharedTexture_DX11;
}

ID2D1RenderTarget* D2D1Renderer::GetRenderTarget() {
	return _d2dRenderTarget;
}

void D2D1Renderer::BeginScene(float red, float green, float blue, float alpha, bool clear) {
	//Release the D3D 11 Device
	_DXGIKeyedMutex_DX11->ReleaseSync(0);

	//Use D3D10.1 device
	_DXGIKeyedMutex_DX10->AcquireSync(0, INFINITE);

///////////DRAW//////////
	_d2dRenderTarget->BeginDraw();

	if(clear) {
		_d2dRenderTarget->Clear(D2D1::ColorF(red, green, blue, alpha));
	}
}

void D2D1Renderer::EndScene() {
	_d2dRenderTarget->EndDraw();
	
	//Release the D3D10.1 Device
	_DXGIKeyedMutex_DX10->ReleaseSync(1);

	//Use the D3D11 Device
	_DXGIKeyedMutex_DX11->AcquireSync(1, INFINITE); //5
}

///////////////////////////////////////////////////////////////////////////////////////////////
//                                           CONTEXT                                         //
///////////////////////////////////////////////////////////////////////////////////////////////

void D2D1Renderer::createDirect2DContext() {
	_direct2DContext = new Direct2DContext(_d2dRenderTarget, _d2dFactory, _dWriteFactory, _textFormat);
}

Direct2DContext* D2D1Renderer::GetDirect2DContext() {
	return _direct2DContext;
}

///////////////////////////////////////////////////////////////////////////////////////////////
//                                           TEXT                                            //
///////////////////////////////////////////////////////////////////////////////////////////////

void D2D1Renderer::createTextLayout() {
	HRESULT hr;
//create DWriteFactory
    hr = DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(_dWriteFactory), //IDWriteFactory
        reinterpret_cast<IUnknown**>(&_dWriteFactory)
    );

	if(FAILED(hr)) {
		return;
	}

//create text format.
	hr = _dWriteFactory->CreateTextFormat(
        L"Verdana", //"Gabriola",
        NULL,
        DWRITE_FONT_WEIGHT_NORMAL, //DWRITE_FONT_WEIGHT_REGULAR,
        DWRITE_FONT_STYLE_NORMAL,
        DWRITE_FONT_STRETCH_NORMAL,
        12.0f,
        L"", //locale //L"en-us",
        &_textFormat
    );

	if(FAILED(hr)) {
		return;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////
//                                     Event Listener                                        //
///////////////////////////////////////////////////////////////////////////////////////////////

void D2D1Renderer::addD2D1FrameRenderListener(ID2D1FrameRenderListener* listener) {
	_D2D1FrameListeners.push_back(listener);
}

void D2D1Renderer::removeD2D1FrameRenderListener(ID2D1FrameRenderListener* listener) {
	for(std::vector<ID2D1FrameRenderListener*>::iterator iter = _D2D1FrameListeners.begin(); iter != _D2D1FrameListeners.end();) {
		if(*iter == listener)
			iter = _D2D1FrameListeners.erase(iter);
		else
			++iter;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////
//                                        FUNCTIONS                                          //
///////////////////////////////////////////////////////////////////////////////////////////////

ID2D1Bitmap* D2D1Renderer::LoadBitmapFromFile(std::wstring filename) {
	HRESULT hr;

	IWICImagingFactory* m_pIWICFactory = 0;

	//create
	hr = CoCreateInstance(
		CLSID_WICImagingFactory1,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_PPV_ARGS(&m_pIWICFactory)
	);

	if(FAILED(hr)) {
		return 0;
	}

	//Create a decoder
	IWICBitmapDecoder* pDecoder = 0;

	//Glyph3::FileSystem fs;
	//filename = fs.GetTextureFolder() + filename;

	hr = m_pIWICFactory->CreateDecoderFromFilename(
       filename.c_str(),                      // Image to be decoded
       NULL,                            // Do not prefer a particular vendor
       GENERIC_READ,                    // Desired read access to the file
       WICDecodeMetadataCacheOnDemand,  // Cache metadata when needed
       &pDecoder                        // Pointer to the decoder
    );

	if(FAILED(hr)) {
		return 0;
	}

   //Retrieve the first frame of the image from the decoder
	IWICBitmapFrameDecode* pFrame = NULL;
	
	if(SUCCEEDED(hr)) {
		hr = pDecoder->GetFrame(0, &pFrame);
	}

//
	IWICFormatConverter* m_pConvertedSourceBitmap = 0;

	// Format convert the frame to 32bppPBGRA
	if(SUCCEEDED(hr)) {
		//if(m_pConvertedSourceBitmap != 0) {
			//m_pConvertedSourceBitmap->Release();
		//}
		
		hr = m_pIWICFactory->CreateFormatConverter(&m_pConvertedSourceBitmap);
	}

	if (SUCCEEDED(hr)) {
       hr = m_pConvertedSourceBitmap->Initialize(
           pFrame,                          // Input bitmap to convert
           GUID_WICPixelFormat32bppPBGRA, //GUID_WICPixelFormat32bppPRGBA //GUID_WICPixelFormat32bppPBGRA,   // Destination pixel format, GUID_WICPixelFormat32bppPRGBA, 
           WICBitmapDitherType::WICBitmapDitherTypeNone,         // Specified dither pattern
           NULL,                            // Specify a particular palette 
           0.f,                             // Alpha threshold
           WICBitmapPaletteTypeCustom       //WICBitmapPaletteTypeMedianCut //Palette translation type
       );
	}

//
	ID2D1Bitmap* bitmap = 0;

	// D2DBitmap may have been released due to device loss. 
    // If so, re-create it from the source bitmap
    if(m_pConvertedSourceBitmap && !bitmap) {
		this->_d2dRenderTarget->CreateBitmapFromWicBitmap(m_pConvertedSourceBitmap, NULL, &bitmap);
    }

	return bitmap;
}

D2D1Renderer* D2D1Renderer::Get() {
	return( m_spRenderer );
}